//
//  SFAttributeView.m
//  CoreText进阶封装
//
//  Created by xueshan1 on 2019/2/18.
//  Copyright © 2019 baiqiang. All rights reserved.
//

#import "SFAttributeView.h"
#import <CoreText/CoreText.h>


typedef NS_ENUM(NSUInteger, SFAttributeViewType) {
    SFAttributeViewType_Text,
    SFAttributeViewType_Image,
    SFAttributeViewType_View
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface SFAttributeViewModel : NSObject

@property(nonatomic,assign)SFAttributeViewType drawType;
//link
@property(nonatomic,assign) NSRange range;
@property(nonatomic,assign) BOOL canClick;
@property(nonatomic,strong) NSDictionary *linkData;
@property(nonatomic,copy) NSString *linkTag;
@property(nonatomic,strong) NSMutableArray * linkRectArray;

//image
@property(nonatomic,strong) UIImage *image;
@property(nonatomic,strong) NSString *imageName;
@property(nonatomic,assign) CGRect  imagePostion;
@property(nonatomic,strong) NSDictionary *imageSizeDic;
//view
@property(nonatomic,strong) UIView *customView;

//line
@property(nonatomic,assign) CTLineRef ctLine;
@property(nonatomic,assign) CGPoint   ctLinePosition;
@property(nonatomic,copy) void(^clickTruncationBlock)(SFAttributeView *view);//查看更多
@property(nonatomic,assign) CGRect truncationFrame;

@end

@implementation SFAttributeViewModel

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//上升高度回调函数
static CGFloat ascentCallback(void *ref) {
    return [(NSNumber *)[(__bridge NSDictionary *)ref objectForKey:@"height"] floatValue];
}
//下降高度回调函数
static CGFloat descentCallback(void *ref) {
    return 0;
}
//文本宽度回调函数
static CGFloat widthCallback(void *ref) {
    return [(NSNumber *)[(__bridge NSDictionary *)ref objectForKey:@"width"] floatValue];
}

NSString *const SFAttributeViewLinkAttributeName = @"SFAttributeViewLinkAttributeName";
NSString *const SFAttributeViewLinkDataKey = @"SFAttributeViewLinkDataKey";
NSString *const SFAttributeViewSpaceAttributeName = @"SFAttributeViewSpaceAttributeName";

typedef NS_ENUM(NSUInteger, SFAttributeViewDrawMode) {
    SFAttributeViewDrawMode_Frame,
    SFAttributeViewDrawMode_Line
};


@interface SFAttributeView (){
    BOOL _needSpread;
}
//config
@property(nonatomic,copy) NSString *string;
@property(nonatomic,strong) UIFont *font;
@property(nonatomic,strong) UIColor *textColor;
@property(nonatomic,strong) NSMutableAttributedString *truncationToken;

//
@property(nonatomic,assign) CTFrameRef ctFrame;
@property(nonatomic,strong) NSMutableAttributedString *attributeString;
@property(nonatomic,strong)NSMutableArray<SFAttributeViewModel *> *highlightArray;
@property(nonatomic,strong)NSMutableArray<SFAttributeViewModel *> *imageArray;
@property(nonatomic,strong)NSMutableArray<SFAttributeViewModel *> *customViewArray;
@property(nonatomic,strong)NSMutableArray<SFAttributeViewModel *> *ctLineArray;
@property(nonatomic,assign)SFAttributeViewDrawMode drawMode;

@end

@implementation SFAttributeView

- (instancetype)initWithFrame:(CGRect)frame string:(NSString *)string font:(UIFont *)font textColor:(UIColor *)textColor{
    return [self initWithFrame:frame string:string font:font textColor:textColor fitRealHeight:YES];
}
- (instancetype)initWithFrame:(CGRect)frame string:(NSString *)string font:(UIFont *)font textColor:(UIColor *)textColor fitRealHeight:(BOOL)fitRealHeight{
    return [self initWithFrame:frame string:string font:font textColor:textColor fitRealHeight:fitRealHeight numberOfLines:0 truncationToken:@""];
}

- (instancetype)initWithFrame:(CGRect)frame string:(NSString *)string font:(UIFont *)font textColor:(UIColor *)textColor fitRealHeight:(BOOL)fitRealHeight numberOfLines:(NSInteger)numberOfLines truncationToken:(NSString *_Nullable)truncationToken{
    self = [super initWithFrame:frame];
    if (self) {
        //default
        _highlightArray = [NSMutableArray array];
        _imageArray = [NSMutableArray array];
        _customViewArray = [NSMutableArray array];
        _ctLineArray = [NSMutableArray array];

        _string          = string;
        _font            = font?:[UIFont systemFontOfSize:15];
        _textColor       = textColor?:[UIColor blackColor];
        _fitRealHeight   = fitRealHeight;
        _numberOfLines        = numberOfLines;
        _needSpread = _numberOfLines==0?YES:NO;
        
        if (truncationToken.length > 0) {
            _truncationToken = [[NSMutableAttributedString alloc]initWithString:truncationToken attributes:[self getDefault_NS_AttributesConfig]];
        }
        _attributeString = [[NSMutableAttributedString alloc]initWithString:_string attributes:[self getDefault_CT_AttributesConfig]];
        
        [self prepareCTFrameWithAttributeString:_attributeString];
        [self prepareTruncatedLinesWithAttributeString:_attributeString];

    }
    return self;
}

- (void)setFitRealHeight:(BOOL)fitRealHeight{
    _fitRealHeight = fitRealHeight;
    [self prepareCTFrameWithAttributeString:_attributeString];
}
- (void)setNumberOfLines:(NSInteger)numberOfLines{
    _numberOfLines = numberOfLines;
    if (numberOfLines == 0) {
        _needSpread = YES;
    }else{
        _needSpread = NO;
    }
    [self prepareCTFrameWithAttributeString:_attributeString];
    [self prepareTruncatedLinesWithAttributeString:_attributeString];

    [self setNeedsDisplay];
}

#pragma mark - prepare frame/run
/**
 生成 ctFrame 
 */
- (void)prepareCTFrameWithAttributeString:(NSAttributedString *)attributeString{
    if (_ctFrame) {
        CFRelease(_ctFrame);
        _ctFrame = nil;
        [self setNeedsDisplay];
    }
    
    //创建CTFrameSetterRef实例
    CTFramesetterRef ctFrameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributeString);
    //获取要绘制的区域信息
    CGSize restrictSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
    CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(ctFrameSetter, CFRangeMake(0, 0), NULL, restrictSize, NULL);
    CGFloat textHeight = coreTextSize.height; //实际高度
    //配置ctframeRef信息
    CGMutablePathRef path = CGPathCreateMutable();
    
    
     if (_fitRealHeight) {
        if (_needSpread) {
            CGPathAddRect(path, NULL, CGRectMake(0, 0, self.frame.size.width, textHeight));
            //显示->实际高度
            CGRect frame = self.frame;
            frame.size.height = textHeight;
            self.frame = frame;
        }else{
            CGFloat height = _font.lineHeight * (1.2 + _numberOfLines);
            CGPathAddRect(path, NULL, CGRectMake(0, 0, self.frame.size.width, height));
            //显示->实际高度
            CGRect frame = self.frame;
            frame.size.height = height;
            self.frame = frame;
        }
        
    }else{
        CGPathAddRect(path, NULL, self.bounds);
    }
    
    
    
    CTFrameRef ctframeRef = CTFramesetterCreateFrame(ctFrameSetter, CFRangeMake(0, 0), path, NULL);
    _ctFrame = ctframeRef;
    
    CFRelease(path);
    CFRelease(ctFrameSetter);
}

- (CFIndex)numberOfLinesToDrawWithCTFrame:(CTFrameRef)ctFrame {
    if (_numberOfLines <= 0) {
        return _numberOfLines;
    }
    return MIN(CFArrayGetCount(CTFrameGetLines(ctFrame)), _numberOfLines);
}
/**
 生成 ctLine
 */
- (void)prepareTruncatedLinesWithAttributeString:(NSAttributedString *)attributeString{
    // 清除旧的数据
    [self.ctLineArray removeAllObjects];
    
    // 获取最终需要绘制的文本行数
    CFIndex numberOfLinesToDraw = [self numberOfLinesToDrawWithCTFrame:self.ctFrame];
    
    if (numberOfLinesToDraw <= 0) {
        self.drawMode = SFAttributeViewDrawMode_Frame;
    } else {
        self.drawMode = SFAttributeViewDrawMode_Line;
        NSArray *lines = (NSArray *)CTFrameGetLines(self.ctFrame);
        
        CGPoint lineOrigins[numberOfLinesToDraw];
        CTFrameGetLineOrigins(self.ctFrame, CFRangeMake(0, numberOfLinesToDraw), lineOrigins);
        
        for (int lineIndex = 0; lineIndex < numberOfLinesToDraw; lineIndex ++) {
            
            CTLineRef line = (__bridge CTLineRef)(lines[lineIndex]);
            CFRange range = CTLineGetStringRange(line);
            // 判断最后一行是否需要显示【截断标识字符串(...)】
            if ( lineIndex == numberOfLinesToDraw - 1
                && range.location + range.length < attributeString.length) {
                
                // 创建【截断标识字符串(...)】
                NSAttributedString *tokenString = nil;
                if (_truncationToken) {
                    [_truncationToken addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, _truncationToken.length)];
                    tokenString = _truncationToken;
                } else {
                    // 只要用到字体大小和颜色的属性，这里如果使用kCTParagraphStyleAttributeName属性在使用boundingRectWithSize方法计算大小的步骤会崩溃
                    NSDictionary *tokenAttributes =@{NSForegroundColorAttributeName:_textColor,
                                                     NSFontAttributeName: _font,
                                                     };
                    //...
                    tokenString = [[NSAttributedString alloc] initWithString:@"\u2026" attributes:tokenAttributes];
                }
                
                // 计算【截断标识字符串(...)】的长度
                CGSize tokenSize = [tokenString boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:NULL].size;
                CGFloat tokenWidth = tokenSize.width;
                CTLineRef truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)tokenString);
                
                // 根据【截断标识字符串(...)】的长度，计算【需要截断字符串】的最后一个字符的位置，把该位置之后的字符从【需要截断字符串】中移除，留出【截断标识字符串(...)】的位置
                CFIndex truncationEndIndex = CTLineGetStringIndexForPosition(line, CGPointMake(self.frame.size.width - tokenWidth, 0));
                CGFloat length = range.location + range.length - truncationEndIndex;
                
                // 把【截断标识字符串(...)】添加到【需要截断字符串】后面
                NSMutableAttributedString *truncationString = [[attributeString attributedSubstringFromRange:NSMakeRange(range.location, range.length)] mutableCopy];
                if (length < truncationString.length) {
                    [truncationString deleteCharactersInRange:NSMakeRange(truncationString.length - length, length)];
                    [truncationString appendAttributedString:tokenString];
                }
                
                // 使用`CTLineCreateTruncatedLine`方法创建含有【截断标识字符串(...)】的`CTLine`对象
                CTLineRef truncationLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationString);
                CTLineTruncationType truncationType = kCTLineTruncationEnd;
                CTLineRef lastLine = CTLineCreateTruncatedLine(truncationLine, self.frame.size.width, truncationType, truncationTokenLine);
                
                SFAttributeViewModel *lineModel = [SFAttributeViewModel new];
                lineModel.ctLine = lastLine;
                lineModel.ctLinePosition = CGPointMake(lineOrigins[lineIndex].x, lineOrigins[lineIndex].y);
                [self.ctLineArray addObject:lineModel];

                // 添加truncation的位置信息
                NSArray *runs = (NSArray *)CTLineGetGlyphRuns(line);
                if (runs.count > 0 && self.clickTruncationBlock) {
                    CTRunRef run = (__bridge CTRunRef)runs.lastObject;
                    
                    CGFloat ascent;
                    CGFloat desent;
                    CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desent, NULL);
                    CGFloat height = ascent + desent;
                    
                    CGRect truncationFrame = CGRectMake(width - tokenWidth,
                                                        self.frame.size.height - lineOrigins[lineIndex].y - height,
                                                        tokenSize.width,
                                                        tokenSize.height);
                    lineModel.truncationFrame = truncationFrame;
                    lineModel.clickTruncationBlock = self.clickTruncationBlock;
                   
                }
 
                CFRelease(truncationTokenLine);
                CFRelease(truncationLine);
                
            } else {
                SFAttributeViewModel *lineModel = [SFAttributeViewModel new];
                lineModel.ctLine = line;
                lineModel.ctLinePosition = CGPointMake(lineOrigins[lineIndex].x, lineOrigins[lineIndex].y);
                [self.ctLineArray addObject:lineModel];
            }
        }
        
        
    }
    
}


//获取图片具体位置信息
- (void)caculateLinkandImagePostion {
    /**
     *  在CTFrame内部是由多个CTline组成，每行CTline又是由多个CTRun组成
     *  每个CTRun代表一组风格一致的文本(CTline和CTRun的创建不需要我们管理)
     *  在CTRun中我们可以设置代理来指定绘制此组文本的宽高和排列方式等信息
     *  此处利用CTRun代理设置一个空白的字符给定宽高，最后在利用CGContextDrawImage将其绘制
     */
    
    //获取CTFrame中所有的CTline
    NSArray *lines = (NSArray *)CTFrameGetLines(self.ctFrame);
    NSInteger lineCount = lines.count;
    //利用CGPoint数组获取所有CTline的起始坐标
    CGPoint lineOrigins[lineCount];
    CTFrameGetLineOrigins(self.ctFrame, CFRangeMake(0, 0), lineOrigins);
    
    //获取图片信息数组中第一个图片信息
    NSInteger imgIndex = 0;

    //遍历每行CTline查找是否含有CTRun代理
    for (int i = 0; i < lineCount; i ++) {
        CTLineRef line = (__bridge CTLineRef)lines[i];
        NSArray * runObjArray = (NSArray *)CTLineGetGlyphRuns(line);
        for (id runObj in runObjArray) {
            //判断该CTRun是否设置有代理,若无代理直接进行下次循环
            CTRunRef run = (__bridge CTRunRef)runObj;
            NSDictionary *runAttributes = (NSDictionary *)CTRunGetAttributes(run);
            //->link key
            NSDictionary *extraData = (NSDictionary *)[runAttributes valueForKey:SFAttributeViewLinkAttributeName];
            if (extraData) {
                SFAttributeViewModel *model = (SFAttributeViewModel *)[extraData objectForKey:SFAttributeViewLinkDataKey];
                
                // 获取CTRun的信息
                CGFloat ascent;
                CGFloat desent;
                // 可以直接从metaData获取到图片的宽度和高度信息
                CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desent, NULL);
                CGFloat height = ascent + desent;
                
                // CTLineGetOffsetForStringIndex获取CTRun的起始位置
                CGFloat xOffset = lineOrigins[i].x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
                CGFloat yOffset = self.frame.size.height - lineOrigins[i].y - ascent;
                
                CGRect uiKitClickableFrame = CGRectMake(xOffset, yOffset, width, height);
                
                if (!model.linkRectArray) {
                    model.linkRectArray = [NSMutableArray array];
                }
                
                [model.linkRectArray addObject:[NSValue valueWithCGRect:uiKitClickableFrame]];
            }
            
            //->delegate
            CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAttributes valueForKey:(id)kCTRunDelegateAttributeName];
            if (delegate == nil) {
                continue;
            }
            //若CTRun有代理，则获取代理信息
            NSDictionary *metaDic = CTRunDelegateGetRefCon(delegate);
            if (![metaDic isKindOfClass:[NSDictionary class]]) {
                continue;
            }
            //找到代理则开始 计算图片位置信息
//            CGRect runBounds;
//            CGFloat ascent;
//            CGFloat desent;
//            runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desent, NULL);
//            runBounds.size.height = ascent + desent;
//            //获取此CTRun在x上的偏移量
//            CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
//            //设置起始坐标点
//            runBounds.origin.x = lineOrigins[i].x + xOffset;
//            runBounds.origin.y = lineOrigins[i].y - desent;
//            //获取CTFrame的路径
//            CGPathRef pathRef = CTFrameGetPath(self.ctFrame);
//            CGRect colRect = CGPathGetBoundingBox(pathRef);
//            //根据runBounds配置图片在绘制视图中的实际位置
//            CGRect delegateBounds = CGRectOffset(runBounds, colRect.origin.x, colRect.origin.y);
//
//            //->view key
//            SFAttributeViewModel *viewModel = (SFAttributeViewModel *)[runAttributes valueForKey:SFAttributeViewSpaceAttributeName];
//            if (viewModel.drawType == SFAttributeViewType_Image) {
//                viewModel.imagePostion = delegateBounds;
//            }else if (viewModel.drawType == SFAttributeViewType_View){
//
//
//                viewModel.customView.frame = delegateBounds;
//            }
            // 找到代理则开始计算图片位置信息
            CGFloat ascent;
            CGFloat desent;
            // 可以直接从metaData获取到图片的宽度和高度信息
            CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desent, NULL);
            CGFloat height = ascent + desent;
            
            // CTLineGetOffsetForStringIndex获取CTRun的起始位置
            CGFloat xOffset = lineOrigins[i].x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
            CGFloat yOffset = lineOrigins[i].y;
            
            //->view key
            SFAttributeViewModel *viewModel = (SFAttributeViewModel *)[runAttributes valueForKey:SFAttributeViewSpaceAttributeName];
            if (viewModel.drawType == SFAttributeViewType_Image) {
                yOffset = yOffset - desent;
                viewModel.imagePostion = CGRectMake(xOffset, yOffset, width, height);
            }else if (viewModel.drawType == SFAttributeViewType_View){
                yOffset = self.frame.size.height - lineOrigins[i].y - ascent;
                viewModel.customView.frame = CGRectMake(xOffset, yOffset, width, height);
            }
            
//            if (imgIndex < self.imageArray.count) {
//                SFAttributeViewModel *imageData = self.imageArray[imgIndex];
//                imageData.imagePostion = delegateBounds;
//            }
            imgIndex++;
            
        }
    }
}
- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];

    if (!_ctFrame) {
        return;
    }
    
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1, -1);
    
    [self caculateLinkandImagePostion];

    [self prepareTruncatedLinesWithAttributeString:_attributeString];
    
    //text
    if (self.drawMode == SFAttributeViewDrawMode_Frame) {
        CTFrameDraw(self.ctFrame, context);

    }else{
        for (SFAttributeViewModel *model in self.ctLineArray) {
            CGContextSetTextPosition(context, model.ctLinePosition.x, model.ctLinePosition.y);
            CTLineDraw(model.ctLine, context);
        }
    }
    
    
    //image
    for (SFAttributeViewModel *model in self.imageArray) {
        CGContextDrawImage(context, model.imagePostion, model.image.CGImage);
    }
    
    //view
    for (SFAttributeViewModel *model in self.customViewArray) {
        [self addSubview:model.customView];
    }

}


- (void)dealloc{
    CFRelease(_ctFrame);

}

- (NSMutableDictionary *)getDefault_CT_AttributesConfig{
    //
    CGFloat lineSpace = 1;
    CTTextAlignment textAlignment = kCTTextAlignmentLeft;
    CTLineBreakMode lineBreakMode = kCTLineBreakByCharWrapping;
    
    const int kSettingsCount = 5;
    CTParagraphStyleSetting theSettings[kSettingsCount] = {
        {kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof(CGFloat), &lineSpace},
        {kCTParagraphStyleSpecifierMinimumLineSpacing, sizeof(CGFloat), &lineSpace},
        {kCTParagraphStyleSpecifierMaximumLineSpacing, sizeof(CGFloat), &lineSpace},
        {kCTParagraphStyleSpecifierAlignment, sizeof(textAlignment), &textAlignment},
        {kCTParagraphStyleSpecifierLineBreakMode, sizeof(kSettingsCount), &lineBreakMode},
    };
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(theSettings, kSettingsCount);
 
    //配置字体信息
    CGFloat fontSize = _font.pointSize;
    CTFontRef ctFont = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
    
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[(id)kCTForegroundColorAttributeName] = (id)_textColor.CGColor;
    dict[(id)kCTFontAttributeName] = (__bridge id)ctFont;
    dict[(id)kCTParagraphStyleAttributeName] = (__bridge id)paragraphStyle;

    CFRelease(paragraphStyle);
    CFRelease(ctFont);
    
    return dict;
}
- (NSMutableDictionary *)getDefault_NS_AttributesConfig{
    //
    CGFloat lineSpace = 1;
  
    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
    paragraphStyle.lineSpacing = lineSpace;
    paragraphStyle.alignment = NSTextAlignmentLeft;
    paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
 
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[NSForegroundColorAttributeName] = _textColor;
    dict[NSFontAttributeName] = _font;
    dict[NSParagraphStyleAttributeName] = paragraphStyle;
    
    return dict;
}


#pragma mark - Link
- (void)addHighlightStringRange:(NSRange)highlightRange highlightColor:(UIColor *)highlightColor{
    [self addHighlightStringRange:highlightRange highlightColor:highlightColor canClick:NO showUnderLine:NO linkData:nil];
}
- (void)addHighlightStringRange:(NSRange)highlightRange highlightColor:(UIColor *)highlightColor canClick:(BOOL)canClick showUnderLine:(BOOL)showUnderLine{
    [self addHighlightStringRange:highlightRange highlightColor:highlightColor canClick:canClick showUnderLine:showUnderLine linkData:nil];
}
- (void)addHighlightStringRange:(NSRange)highlightRange highlightColor:(UIColor *)highlightColor canClick:(BOOL)canClick showUnderLine:(BOOL)showUnderLine linkData:(NSDictionary *_Nullable)linkData{
    [_attributeString addAttribute:(id)kCTForegroundColorAttributeName value:(__bridge id)highlightColor.CGColor range:highlightRange];
    
    if (showUnderLine) {
        [_attributeString addAttribute:(id)kCTUnderlineColorAttributeName value:(id)highlightColor.CGColor range:highlightRange];
        [_attributeString addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInt:kCTUnderlineStyleSingle] range:highlightRange];
    }

    SFAttributeViewModel *highlightModel = [SFAttributeViewModel new];
    highlightModel.range = highlightRange;
    highlightModel.canClick = canClick;
    highlightModel.linkData = linkData;
    highlightModel.drawType = SFAttributeViewType_Text;
    [self.highlightArray addObject:highlightModel];
    
    if (canClick) {
        NSMutableDictionary *mutableDic = [[NSMutableDictionary alloc]initWithDictionary:linkData];
        [mutableDic setObject:highlightModel forKey:SFAttributeViewLinkDataKey];
        [_attributeString addAttribute:SFAttributeViewLinkAttributeName value:mutableDic range:highlightRange];
    }
    
    [self prepareCTFrameWithAttributeString:_attributeString];
    [self setNeedsDisplay];
}

#pragma mark - Image
/**
 图文混排 - 插入图片
 */
- (void)appendImageName:(NSString *)imageName  imageSize:(CGSize)imageSize canClick:(BOOL)canClick{
    [self insertImageWithLocation:_attributeString.length imageName:imageName imageSize:imageSize canClick:canClick];
}
- (void)insertImageWithLocation:(NSUInteger)location imageName:(NSString *)imageName imageSize:(CGSize)imageSize canClick:(BOOL)canClick{
    if (!imageName.length) {
        return;
    }
    
    NSDictionary *dic = @{@"width":[NSString stringWithFormat:@"%f",imageSize.width],
                          @"height":[NSString stringWithFormat:@"%f",imageSize.height]
                          } ;
    
    SFAttributeViewModel *imageModel = [SFAttributeViewModel new];
    imageModel.image = [UIImage imageNamed:imageName];
    imageModel.imageName = imageName;
    imageModel.canClick = canClick;
    imageModel.imageSizeDic = dic;
    imageModel.drawType = SFAttributeViewType_Image;
    [self.imageArray addObject:imageModel];
    

    NSAttributedString *spaceAttSting = [self getSpaceAttributeStringWithDictionary:dic viewModel:imageModel];
    
    [_attributeString insertAttributedString:spaceAttSting atIndex:location];
    
    [self prepareCTFrameWithAttributeString:_attributeString];
    
    
    [self setNeedsDisplay];
    
}

/**
 *  返回空白的占位符文字
 *
 *  @return 空白占位符(有宽高)
 */
- (NSAttributedString *)getSpaceAttributeStringWithDictionary:(NSDictionary *)dict viewModel:(SFAttributeViewModel *)viewModel{
    //配置CTRun代理回调函数
    CTRunDelegateCallbacks callbacks;
    //为callbacks开辟内存空间
    memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks));
    //设置代理版本
    callbacks.version = kCTRunDelegateVersion1;
    //配置代理回调函数
    callbacks.getAscent = ascentCallback;
    callbacks.getDescent = descentCallback;
    callbacks.getWidth = widthCallback;
    //根据配置的代理信息创建代理
    CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void*)dict);
    //使用0xFFFC作为空白占位符
    unichar objectReplacementChar = 0xFFFC;
    NSString *content = [NSString stringWithCharacters:&objectReplacementChar length:1];
    NSMutableDictionary * attributes = [self getDefault_CT_AttributesConfig];
   
    NSMutableAttributedString *space = [[NSMutableAttributedString alloc] initWithString:content attributes:attributes];
    //test
    [space addAttribute:SFAttributeViewSpaceAttributeName value:viewModel range:NSMakeRange(0, 1)];
 
    //为属性文字设置代理
    CFAttributedStringSetAttribute((CFMutableAttributedStringRef)space, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
    CFRelease(delegate);
    return space;
}

#pragma mark - view
- (void)insertCustomView:(UIView *)customView inLocation:(NSUInteger)location{
    if (!customView) {
        return;
    }
//    _fitRealHeight = YES;

    NSDictionary *dic = @{@"width":[NSString stringWithFormat:@"%f",customView.frame.size.width],
                          @"height":[NSString stringWithFormat:@"%f",customView.frame.size.height]
                          } ;
    SFAttributeViewModel *viewModel = [SFAttributeViewModel new];
    viewModel.customView = customView;
    viewModel.drawType = SFAttributeViewType_View;
    viewModel.imageSizeDic = dic;//不应用会造被提前释放
    [self.customViewArray addObject:viewModel];
    
    NSAttributedString *spaceAttSting = [self getSpaceAttributeStringWithDictionary:dic viewModel:viewModel];

    [_attributeString insertAttributedString:spaceAttSting atIndex:location];

    [self prepareCTFrameWithAttributeString:_attributeString];
 
    [self setNeedsDisplay];
}

#pragma mark - touches

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    
    //link
//    SFAttributeViewModel *linkModel = [self linkCanResponseWithPoint:point];
//    if (linkModel) {
//        NSLog(@"click link");
//    }
    for (SFAttributeViewModel *model in _highlightArray) {
        for (NSValue *rectValue in model.linkRectArray) {
            if (CGRectContainsPoint(rectValue.CGRectValue, point)) {
                NSLog(@"click link");
                [self addClickableCoverWithItem:model.linkRectArray];
                
                if (self.delegate && [self.delegate respondsToSelector:@selector(attributeView:clickLinkWithLinkData:)]) {
                    [self.delegate attributeView:self clickLinkWithLinkData:model.linkData];
                }
                break;
            }
        }
        
    }
    
    //ctLine
    for (SFAttributeViewModel *model in self.ctLineArray) {
        if (model.clickTruncationBlock) {
            model.clickTruncationBlock(self);
        }
    }
    
    //image
    for (SFAttributeViewModel *model in self.imageArray) {
        //翻转坐标系imageArray中所存坐标的坐标系为CoreText坐标系
        CGRect imageRect = model.imagePostion;
        CGPoint imagePositon = imageRect.origin;
        imagePositon.y = self.bounds.size.height - imageRect.size.height - imageRect.origin.y;
        imageRect = CGRectMake(imagePositon.x, imagePositon.y, imageRect.size.width, imageRect.size.height);
        //判断点击点是否在Rect当中
        if (CGRectContainsPoint(imageRect, point)) {
            NSLog(@"click image");
            if (self.delegate && [self.delegate respondsToSelector:@selector(attributeView:clickImageWithImageName:)]) {
                [self.delegate attributeView:self clickImageWithImageName:model.imageName];
            }
            return;
        }
    }
    
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self removeClickableCoverView];
    });
    
}

/**
 判断点击处 index 是否在 link范围内
 */
- (SFAttributeViewModel *)linkCanResponseWithPoint:(CGPoint)point{
    CFArrayRef lines = CTFrameGetLines(_ctFrame);
    if (lines == nil) {
        return nil;
    }
    
    CFIndex linesCount = CFArrayGetCount(lines);
   
    CGPoint linesOrigins[linesCount];
    CTFrameGetLineOrigins(_ctFrame, CFRangeMake(0, 0), linesOrigins);
    
    //由于CoreText和UIKit坐标系不同所以要做个对应转换
    CGAffineTransform transform = CGAffineTransformMakeTranslation(0, self.bounds.size.height);
    transform = CGAffineTransformScale(transform, 1, -1);
    SFAttributeViewModel *model = nil;
    for (int i = 0; i < linesCount; i ++) {
        CGPoint linePoint = linesOrigins[i];
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        //获取当前行的rect信息
        CGRect flippedRect = [self getLineBounds:line point:linePoint];
        //将CoreText坐标转换为UIKit坐标
        CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);
        //判断点是否在Rect当中
        if (CGRectContainsPoint(rect, point)) {
            //获取点在line行中的位置
            CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect));
            //获取点中字符在line中的位置(在--属性文字---中是第几个字)
            CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
            //判断此字符是否在链接属性文字当中
            model = [self touchIndexIsLink:idx];
        }
    }
    return model;
}
- (CGRect)getLineBounds:(CTLineRef)line point:(CGPoint)point {
    CGFloat ascent = 0;
    CGFloat descent = 0;
    CGFloat leading = 0;
    //在获取line行的宽度信息的同时得到其他信息
    CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
    CGFloat height = ascent + descent;
    return CGRectMake(point.x, point.y, width, height);
}
- (SFAttributeViewModel *)touchIndexIsLink:(CFIndex)index{
    for (SFAttributeViewModel *model in self.highlightArray) {
        if (NSLocationInRange(index, model.range) && model.canClick) {
            return model;
        }
    }
    return nil;
}

- (void)addClickableCoverWithItem:(NSArray <NSValue *>*)rectArray {
//    CGFloat red = 0.3;
    for (NSValue *rectValue in rectArray) {
        UIView *coverView = [[UIView alloc] initWithFrame:rectValue.CGRectValue];
        coverView.backgroundColor = [UIColor colorWithRed:0.3  green:1 blue:1 alpha:0.3];
        coverView.layer.cornerRadius = 3;
        coverView.tag = 9911;
        [self insertSubview:coverView atIndex:0];
    }
    
}
- (void)removeClickableCoverView {
    for (UIView *subView in self.subviews) {
        if (subView.tag == 9911) {
            [subView removeFromSuperview];
        }
    }
}

- (UIImage*)viewToImage:(UIView *)view{
   
    UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, 0.0);

    if ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) {
        [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
    }else{
        [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    }
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}


@end
